/** @file
  Unit tests for the implementation of SecureBootVariableLib.

  Copyright (c) 2022, Intel Corporation. All rights reserved.
  SPDX-License-Identifier: BSD-2-Clause-Patent
**/
#include <Library/GoogleTestLib.h>
#include <GoogleTest/Library/MockUefiLib.h>
#include <GoogleTest/Library/MockUefiRuntimeServicesTableLib.h>

extern "C" {
  #include <Uefi.h>
  #include <UefiSecureBoot.h>
  #include <Guid/AuthenticatedVariableFormat.h>
  #include <Guid/ImageAuthentication.h>
  #include <Library/SecureBootVariableLib.h>
  #include <Library/MemoryAllocationLib.h>
}

using namespace testing;

//////////////////////////////////////////////////////////////////////////////
class SetSecureBootModeTest : public Test {
  protected:
    MockUefiRuntimeServicesTableLib RtServicesMock;
    UINT8       SecureBootMode;
    EFI_STATUS  Status;

    void SetUp() override {
      // Any random magic number can be used for these tests
      SecureBootMode = 0xAB;
    }
};

// Test SetSecureBootMode() API from SecureBootVariableLib to verify the
// expected error is returned when the call to gRT->SetVariable() fails.
TEST_F(SetSecureBootModeTest, SetVarError) {
  EXPECT_CALL(RtServicesMock, gRT_SetVariable)
    .WillOnce(Return(EFI_INVALID_PARAMETER));

  Status = SetSecureBootMode(SecureBootMode);
  EXPECT_EQ(Status, EFI_INVALID_PARAMETER);
}

// Test SetSecureBootMode() API from SecureBootVariableLib to verify the
// expected secure boot mode is written to the correct variable in the call
// to gRT->SetVariable().
TEST_F(SetSecureBootModeTest, PropogateModeToSetVar) {
  EXPECT_CALL(RtServicesMock,
    gRT_SetVariable(
      Char16StrEq(EFI_CUSTOM_MODE_NAME),
      BufferEq(&gEfiCustomModeEnableGuid, sizeof(EFI_GUID)),
      EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS,
      sizeof(SecureBootMode),
      BufferEq(&SecureBootMode, sizeof(SecureBootMode))))
    .WillOnce(Return(EFI_SUCCESS));

  Status = SetSecureBootMode(SecureBootMode);
  EXPECT_EQ(Status, EFI_SUCCESS);
}

//////////////////////////////////////////////////////////////////////////////
class GetSetupModeTest : public Test {
  protected:
    MockUefiRuntimeServicesTableLib RtServicesMock;
    UINT8       SetupMode;
    EFI_STATUS  Status;
    UINT8       ExpSetupMode;

    void SetUp() override {
      // Any random magic number can be used for these tests
      ExpSetupMode = 0xAB;
    }
};

// Test GetSetupMode() API from SecureBootVariableLib to verify the expected
// error is returned when the call to gRT->GetVariable() fails.
TEST_F(GetSetupModeTest, GetVarError) {
  EXPECT_CALL(RtServicesMock, gRT_GetVariable)
    .WillOnce(Return(EFI_INVALID_PARAMETER));

  Status = GetSetupMode (&SetupMode);
  EXPECT_EQ(Status, EFI_INVALID_PARAMETER);
}

// Test GetSetupMode() API from SecureBootVariableLib to verify the expected
// setup mode is returned (and with a success return code) when the mode is
// successfully read from the call to gRT->GetVariable().
TEST_F(GetSetupModeTest, FetchModeFromGetVar) {
  EXPECT_CALL(RtServicesMock,
    gRT_GetVariable(
      Char16StrEq(EFI_SETUP_MODE_NAME),
      BufferEq(&gEfiGlobalVariableGuid, sizeof(EFI_GUID)),
      _,
      Pointee(Eq(sizeof(SetupMode))),
      NotNull()))
    .WillOnce(DoAll(
      SetArgPointee<3>(sizeof(ExpSetupMode)),
      SetArgBuffer<4>(&ExpSetupMode, sizeof(ExpSetupMode)),
      Return(EFI_SUCCESS)));

  Status = GetSetupMode (&SetupMode);
  ASSERT_EQ(Status, EFI_SUCCESS);
  EXPECT_EQ(SetupMode, ExpSetupMode);
}

//////////////////////////////////////////////////////////////////////////////
class IsSecureBootEnabledTest : public Test {
  protected:
    MockUefiLib UefiLibMock;
    BOOLEAN     Enabled;
};

// Test IsSecureBootEnabled() API from SecureBootVariableLib to verify FALSE
// is returned when the call to GetEfiGlobalVariable2() fails.
TEST_F(IsSecureBootEnabledTest, GetVarError) {
  EXPECT_CALL(UefiLibMock, GetEfiGlobalVariable2)
    .WillOnce(Return(EFI_ABORTED));

  Enabled = IsSecureBootEnabled ();
  EXPECT_EQ(Enabled, FALSE);
}

//////////////////////////////////////////////////////////////////////////////
class IsSecureBootEnabledAllocTest : public IsSecureBootEnabledTest {
  protected:
    UINT8 *BootEnabledBuffer;

    void SetUp() override {
      BootEnabledBuffer = (UINT8*) AllocatePool(1);
      ASSERT_NE(BootEnabledBuffer, nullptr);
    }
};

// Test IsSecureBootEnabled() API from SecureBootVariableLib to verify TRUE
// is returned when the call to GetEfiGlobalVariable2() is successful and
// returns SECURE_BOOT_MODE_ENABLE.
TEST_F(IsSecureBootEnabledAllocTest, IsEnabled) {
  *BootEnabledBuffer = SECURE_BOOT_MODE_ENABLE;
  EXPECT_CALL(UefiLibMock,
    GetEfiGlobalVariable2(
      Char16StrEq(EFI_SECURE_BOOT_MODE_NAME),
      NotNull(),
      _))
    .WillOnce(DoAll(
      SetArgBuffer<1>(&BootEnabledBuffer, sizeof(VOID*)),
      Return(EFI_SUCCESS)));

  Enabled = IsSecureBootEnabled ();
  EXPECT_EQ(Enabled, TRUE);
}

// Test IsSecureBootEnabled() API from SecureBootVariableLib to verify FALSE
// is returned when the call to GetEfiGlobalVariable2() is successful and
// returns SECURE_BOOT_MODE_DISABLE.
TEST_F(IsSecureBootEnabledAllocTest, IsDisabled) {
  *BootEnabledBuffer = SECURE_BOOT_MODE_DISABLE;
  EXPECT_CALL(UefiLibMock,
    GetEfiGlobalVariable2(
      Char16StrEq(EFI_SECURE_BOOT_MODE_NAME),
      NotNull(),
      _))
    .WillOnce(DoAll(
      SetArgBuffer<1>(&BootEnabledBuffer, sizeof(VOID*)),
      Return(EFI_SUCCESS)));

  Enabled = IsSecureBootEnabled ();
  EXPECT_EQ(Enabled, FALSE);
}

int main(int argc, char* argv[]) {
  testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
